home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 37 / IOPROG_37.ISO / SOFT / Multilizer.exe / disk1 / data1.cab / data1 / [Group9]VCL Source Standard / ivsocket.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-08-12  |  9.0 KB  |  388 lines

  1. unit IvSocket;
  2.  
  3. {$I IVMULTI.INC}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows, SysUtils, Messages, Classes, WinSock,
  9.   IvSynchr;
  10.  
  11. type
  12.   EIvSocketError = class(Exception);
  13.  
  14.   TIvWinSocket = class(TObject)
  15.   private
  16.     FHost: String;
  17.     FAddress: String;
  18.     FService: String;
  19.     FPort: Integer;
  20.     FConnected: Boolean;
  21.     FSocket: TSocket;
  22.     FAddr: TSockAddrIn;
  23.     FSocketLock: TIvCriticalSection;
  24.  
  25.   protected
  26.     function InitSocket(
  27.       var name, address, service: String;
  28.       port: Word;
  29.       client: Boolean): TSockAddrIn;
  30.     procedure Disconnect(Socket: TSocket); virtual;
  31.  
  32.   public
  33.     constructor Create;
  34.     destructor Destroy; override;
  35.  
  36.     procedure Open;
  37.     procedure Close;
  38.  
  39.     procedure Lock;
  40.     procedure Unlock;
  41.  
  42.     function LookupName(const name: string) : TInAddr;
  43.     function LookupService(const service: string): Integer;
  44.  
  45.     property Port: Integer read FPort write FPort;
  46.     property Host: String read FHost write FHost;
  47.     property Address: String read FAddress write FAddress;
  48.     property Connected: Boolean read FConnected;
  49.     property Addr: TSockAddrIn read FAddr;
  50.     property Handle: TSocket read FSocket;
  51.   end;
  52.  
  53.   TIvWinSocketStream = class(TStream)
  54.   private
  55.     FSocket: TIvWinSocket;
  56.     FTimeout: Longint;
  57.     FEvent: TIvSimpleEvent;
  58.  
  59.   public
  60.     constructor Create(socket: TIvWinSocket; timeOut: Longint);
  61.     destructor Destroy; override;
  62.  
  63.     function WaitForData(timeout: Longint): Boolean;
  64.     function Read(var buffer; count: Longint): Longint; override;
  65.     function Write(const buffer; count: Longint): Longint; override;
  66.     function Seek(offset: Longint; origin: Word): Longint; override;
  67.  
  68.     function ReadMessage(timeout: Integer): String;
  69.  
  70.     property TimeOut: Longint read FTimeout write FTimeout;
  71.   end;
  72.  
  73. implementation
  74.  
  75. uses
  76.   Forms;
  77.  
  78. const
  79.   sWindowsSocketError = 'Windows socket error: %s (%d), on API ''%s''';
  80.   sNoAddress = 'No address specified';
  81.   sSocketAlreadyOpen = 'Socket already open';
  82.   sCannotCreateSocket = 'Can''t create new socket';
  83.   sSocketIOError = '%s error %d, %s';
  84.   sSocketRead = 'Read';
  85.   sSocketWrite = 'Write';
  86.  
  87. var
  88.   WSAData: TWSAData;
  89.  
  90. function CheckSocketResult(ResultCode: Integer; const Op: string): Integer;
  91. begin
  92.   if ResultCode <> 0 then
  93.   begin
  94.     Result := WSAGetLastError;
  95.     if Result <> WSAEWOULDBLOCK then
  96.       raise EIvSocketError.CreateFmt(
  97.         sWindowsSocketError,
  98.         [SysErrorMessage(Result), Result, Op]);
  99.   end
  100.   else
  101.     Result := 0;
  102. end;
  103.  
  104. procedure Startup;
  105. var
  106.   ErrorCode: Integer;
  107. begin
  108.   ErrorCode := WSAStartup($0101, WSAData);
  109.   if ErrorCode <> 0 then
  110.     raise EIvSocketError.CreateFmt(
  111.       sWindowsSocketError,
  112.       [SysErrorMessage(ErrorCode), ErrorCode, 'WSAStartup']);
  113. end;
  114.  
  115. procedure Cleanup;
  116. var
  117.   ErrorCode: Integer;
  118. begin
  119.   ErrorCode := WSACleanup;
  120.   if ErrorCode <> 0 then
  121.     raise EIvSocketError.CreateFmt(
  122.       sWindowsSocketError,
  123.       [SysErrorMessage(ErrorCode), ErrorCode, 'WSACleanup']);
  124. end;
  125.  
  126. { TCustomWinSocket }
  127.  
  128. constructor TIvWinSocket.Create;
  129. begin
  130.   inherited Create;
  131.   Startup;
  132.   FSocketLock := TIvCriticalSection.Create;
  133.   FSocket := INVALID_SOCKET;
  134.   FAddr.sin_family := PF_INET;
  135.   FAddr.sin_addr.s_addr := INADDR_ANY;
  136.   FAddr.sin_port := 0;
  137.   FConnected := FSocket <> INVALID_SOCKET;
  138. end;
  139.  
  140. destructor TIvWinSocket.Destroy;
  141. begin
  142.   if FConnected and (FSocket <> INVALID_SOCKET) then
  143.     Disconnect(FSocket);
  144.   FSocketLock.Free;
  145.   Cleanup;
  146.   inherited Destroy;
  147. end;
  148.  
  149. procedure TIvWinSocket.Close;
  150. begin
  151.   Disconnect(FSocket);
  152. end;
  153.  
  154. procedure TIvWinSocket.Lock;
  155. begin
  156.   FSocketLock.Enter;
  157. end;
  158.  
  159. procedure TIvWinSocket.Unlock;
  160. begin
  161.   FSocketLock.Leave;
  162. end;
  163.  
  164. function TIvWinSocket.LookupName(const Name: string): TInAddr;
  165. var
  166.   HostEnt: PHostEnt;
  167.   InAddr: TInAddr;
  168. begin
  169.   HostEnt := gethostbyname(PChar(Name));
  170.   FillChar(InAddr, SizeOf(InAddr), 0);
  171.   if HostEnt <> nil then
  172.   begin
  173.     with InAddr, HostEnt^ do
  174.     begin
  175. {$IFDEF IVWIDE}
  176.       S_un_b.s_b1 := h_addr^[0];
  177.       S_un_b.s_b2 := h_addr^[1];
  178.       S_un_b.s_b3 := h_addr^[2];
  179.       S_un_b.s_b4 := h_addr^[3];
  180. {$ELSE}
  181.       S_un_b.s_b1 := h_addr_list^[0];
  182.       S_un_b.s_b2 := h_addr_list^[1];
  183.       S_un_b.s_b3 := h_addr_list^[2];
  184.       S_un_b.s_b4 := h_addr_list^[3];
  185. {$ENDIF}
  186.     end;
  187.   end;
  188.   Result := InAddr;
  189. end;
  190.  
  191. function TIvWinSocket.LookupService(const Service: string): Integer;
  192. var
  193.   ServEnt: PServEnt;
  194. begin
  195.   ServEnt := getservbyname(PChar(Service), 'tcp');
  196.   if ServEnt <> nil then
  197.     Result := ntohs(ServEnt.s_port)
  198.   else Result := 0;
  199. end;
  200.  
  201. function TIvWinSocket.InitSocket(
  202.   var name, address, service: String;
  203.   port: Word;
  204.   client: Boolean): TSockAddrIn;
  205. begin
  206.   Result.sin_family := PF_INET;
  207.  
  208.   if Name <> '' then
  209.     Result.sin_addr := LookupName(name)
  210.   else if Address <> '' then
  211.     Result.sin_addr.s_addr := inet_addr(PChar(Address))
  212.   else if not Client then
  213.     Result.sin_addr.s_addr := INADDR_ANY
  214.   else
  215.     raise EIvSocketError.Create(sNoAddress);
  216.  
  217.   if Service <> '' then
  218.     Result.sin_port := htons(LookupService(Service))
  219.   else
  220.     Result.sin_port := htons(Port);
  221. end;
  222.  
  223. procedure TIvWinSocket.Open;
  224. var
  225.   Blocking: Longint;
  226.   SockAddrIn: TSockAddrIn;
  227. begin
  228.   if FConnected then
  229.     raise EIvSocketError.Create(sSocketAlreadyOpen);
  230.   FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
  231.   if FSocket = INVALID_SOCKET then
  232.     raise EIvSocketError.Create(sCannotCreateSocket);
  233.  
  234.   try
  235.     SockAddrIn := InitSocket(FHost, FAddress, FService, FPort, True);
  236.     Blocking := 0;
  237.     ioctlsocket(FSocket, FIONBIO, Blocking);
  238.     CheckSocketResult(connect(FSocket, SockAddrIn, SizeOf(SockAddrIn)), 'connect');
  239.     FConnected := FSocket <> INVALID_SOCKET;
  240.   except
  241.     Disconnect(FSocket);
  242.     raise;
  243.   end;
  244. end;
  245.  
  246. procedure TIvWinSocket.Disconnect(Socket: TSocket);
  247. begin
  248.   Lock;
  249.   try
  250.     if (Socket = INVALID_SOCKET) or (Socket <> FSocket) then
  251.       exit;
  252.     CheckSocketResult(closesocket(FSocket), 'closesocket');
  253.     FSocket := INVALID_SOCKET;
  254.     FConnected := False;
  255.   finally
  256.     Unlock;
  257.   end;
  258. end;
  259.  
  260. { TWinSocketStream }
  261.  
  262. constructor TIvWinSocketStream.Create(socket: TIvWinSocket; timeOut: Longint);
  263. begin
  264.   FSocket := socket;
  265.   FTimeOut := timeOut;
  266.   FEvent := TIvSimpleEvent.Create;
  267.   inherited Create;
  268. end;
  269.  
  270. destructor TIvWinSocketStream.Destroy;
  271. begin
  272.   FEvent.Free;
  273.   inherited Destroy;
  274. end;
  275.  
  276. function TIvWinSocketStream.WaitForData(timeout: Longint): Boolean;
  277. var
  278.   FDSet: TFDSet;
  279.   TimeVal: TTimeVal;
  280. begin
  281.   TimeVal.tv_sec := Timeout div 1000;
  282.   TimeVal.tv_usec := (Timeout mod 1000)*1000;
  283.   FDSet.fd_count := 0;
  284.   if FDSet.fd_count < FD_SETSIZE then
  285.   begin
  286.     FDSet.fd_array[FDSet.fd_count] := FSocket.Handle;
  287.     Inc(FDSet.fd_count);
  288.   end;
  289.   Result := select(0, @FDSet, nil, nil, @TimeVal) > 0;
  290. end;
  291.  
  292. function TIvWinSocketStream.Read(var Buffer; Count: Longint): Longint;
  293. var
  294.   Overlapped: TOverlapped;
  295.   ErrorCode: Integer;
  296.   dw: DWord;
  297. begin
  298.   Result := 0;
  299.   FSocket.Lock;
  300.   try
  301.     FillChar(Overlapped, SizeOf(Overlapped), 0);
  302.     Overlapped.hEvent := FEvent.Handle;
  303.     if not ReadFile(FSocket.Handle, Buffer, Count, dw, @Overlapped) and
  304.       (GetLastError <> ERROR_IO_PENDING) then
  305.     begin
  306.       ErrorCode := GetLastError;
  307.       raise EIvSocketError.CreateFmt(
  308.         sSocketIOError,
  309.         [sSocketRead, ErrorCode, SysErrorMessage(ErrorCode)]);
  310.     end;
  311.     Result := dw;
  312.  
  313.     if FEvent.WaitFor(FTimeOut) <> ivwrSignaled then
  314.       Result := 0
  315.     else
  316.     begin
  317.       GetOverlappedResult(FSocket.Handle, Overlapped, dw, False);
  318.       Result := dw;
  319.       FEvent.ResetEvent;
  320.     end;
  321.   finally
  322.     FSocket.Unlock;
  323.   end;
  324. end;
  325.  
  326. function TIvWinSocketStream.Write(const Buffer; Count: Longint): Longint;
  327. var
  328.   Overlapped: TOverlapped;
  329.   ErrorCode: Integer;
  330.   dw: DWord;
  331. begin
  332.   Result := 0;
  333.   FSocket.Lock;
  334.   try
  335.     FillChar(OVerlapped, SizeOf(Overlapped), 0);
  336.     Overlapped.hEvent := FEvent.Handle;
  337.     if not WriteFile(FSocket.Handle, Buffer, Count, dw,
  338.       @Overlapped) and (GetLastError <> ERROR_IO_PENDING) then
  339.     begin
  340.       ErrorCode := GetLastError;
  341.       raise EIvSocketError.CreateFmt(
  342.         sSocketIOError,
  343.         [sSocketWrite, ErrorCode, SysErrorMessage(ErrorCode)]);
  344.     end;
  345.     Result := dw;
  346.  
  347.     if FEvent.WaitFor(FTimeOut) <> ivwrSignaled then
  348.       Result := 0
  349.     else
  350.     begin
  351.       GetOverlappedResult(FSocket.Handle, Overlapped, dw, False);
  352.       Result := dw;
  353.     end;
  354.   finally
  355.     FSocket.Unlock;
  356.   end;
  357. end;
  358.  
  359. function TIvWinSocketStream.Seek(Offset: Longint; Origin: Word): Longint;
  360. begin
  361.   Result := 0;
  362. end;
  363.  
  364. function TIvWinSocketStream.ReadMessage(timeout: Integer): String;
  365. const
  366.   SEGMENT_C = 256;
  367. var
  368.   str: String;
  369.   bytesRead: Integer;
  370. begin
  371.   Result := '';
  372.   bytesRead := 0;
  373.   repeat
  374.     if WaitForData(timeout) then
  375.     begin
  376.       bytesRead := Read(str[1], SEGMENT_C);
  377.       if bytesRead > 0 then
  378.       begin
  379.         SetLength(str, bytesRead);
  380.         Result := Result + str;
  381.       end;
  382.     end;
  383.   until bytesRead <= SEGMENT_C;
  384. end;
  385.  
  386. end.
  387.  
  388.